package tool.codescan.mappers;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import tool.data.TypeDefinition;


	/**
	 * @author Jamie
	 * @date 2013/11/14
	 * 
	 * Represents a package/folder in a source code directory.
	 * Used by {@link PackageMapper} to represent a parsed directory structure.
	 * 
	 * @date 2013/12/3
     * UNRESOLVED_PATH exists in multiple places - a bad smell, but it seems unnecessary to have it
     * pulled out as an enum or a path-wrapping class for the moment.
     * 
     * @date
     * 2013/12/03
     * Removed UNRESOLVED_PATH constant as function superseded by {@link TypeDefinition}
     * 
	 */

public class PackageNode {
    
	private String name;
	private File thisLocation;
	private PackageNode parent;
	private List<PackageNode> children;
	private List<TypeNode> files;
	
	
	public PackageNode (File location, PackageNode parentNode) {
		
	    
		this.name = extractName(location);
		this.thisLocation = location;
		this.parent = parentNode;
		children = new ArrayList<>();
		files = new ArrayList<>();
		
		doRecursiveMapping(thisLocation);
		
	}
	
	
	/**
     * Checks for and returns a full (package) path to a type, if the type exists in the package(s)
     * represented by this object.
     * @param typeName - the object representing the name of the Type to look for
     * @param packageNames - the list of imports which may hint at the location of the desired type
     * @return - TypeDefinition - updated object representing the name of the parameter if any 
     *           relevant path information is found.
     *                    
     * Note: the packageNames are used to speed the search process by searching only the 
     * sub-packages of the import string.  If no packageNames are supplied, it is assumed that the
     * entire package mapped by this object has been (star/wildcard) imported and an exhaustive
     * search is carried out.
     */
    public TypeDefinition getPathForType (TypeDefinition typeName, List<String> packageNames) {
        
        TypeDefinition reply = typeName;
        
        if (packageNames.isEmpty()) {  // No clues, check everything... 
            // Search files...
            reply = checkLocalFolderForMatchingFile(typeName, packageNames);
            // ... if no match, search sub-packages
            if (!reply.hasResolvedPath()) {
                reply = checkSubPackagesForMatchingFile(typeName, packageNames);
            }
            
        } else {  
            
            if (packageNames.get(0).equals(name)) {
                // We are on the right track...
                packageNames.remove(0); // ...so remove the name of this node from the list
                 
                if (packageNames.isEmpty()) {  // Clues stop here...
                    
                    // Search files...
                    reply = checkLocalFolderForMatchingFile(typeName, packageNames);
                    // ... if no match, search sub-packages
                    if (!reply.hasResolvedPath()) {
                        reply = checkSubPackagesForMatchingFile(typeName, packageNames);
                    }
                      
                } else {  // The clue trail goes further, pass to child packages only...
                    
                    reply = checkSubPackagesForMatchingFile(typeName, packageNames);
                    
                }
                
            }
            // ...else do nothing - this package node does not match the import package 'hints' 
        }
        
        return reply;
        
    }
	
	
	private TypeDefinition checkLocalFolderForMatchingFile (TypeDefinition typeName, List<String> packageNames) {

        TypeDefinition reply = typeName;
        
        for (TypeNode file : files) {
            
             reply = file.getPathForType(typeName, packageNames);
            
            if (reply.hasResolvedPath()) {
                
                break; 
            
            }
            
        }
        
        return reply;
        
    }

	
	   
    private TypeDefinition checkSubPackagesForMatchingFile ( TypeDefinition typeName, 
                                                                       List<String> packageNames) {

        TypeDefinition reply = typeName;
        
        for (PackageNode child : children) {
            
            reply = child.getPathForType(typeName, packageNames);
            
            if (reply.hasResolvedPath()) {
            
                break;
            
            }
            
        }

        return reply;
    }
	

    private String extractName (File file) {
		
		return file.getName();
		
	}
	
	
	private void doRecursiveMapping (File location) {
		
		File[] contents = location.listFiles();

		for (File f : contents) {
	
			if (f.isDirectory()) {
		
				handleNewNode(f);
		
			} else {
		
				handleNewLeaf(f);
		
			}
			
		}
		
	}
	
	
	private void handleNewNode (File location) {
		
		children.add(new PackageNode(location, this));
		
	}

	
	private void handleNewLeaf (File location) {
		
		String leafName = extractName(location);
		
		if (leafName.endsWith(".java")) {
			
			leafName = leafName.substring(0, leafName.indexOf('.'));
			
			files.add(new TypeNode(leafName, this));
		}
		
	}
	
	
	@Override
	public String toString () {
		
		String reply = "\nPackage: " + name + " (" + children.size() + " subfolders, " + 
																		files.size() + " types)";
		
		for (TypeNode t : files) {
			
			reply += ("\n\t" + t.toString());
			
		}
		
		for (PackageNode p : children) {
			
			reply += ("\n" + p.toString());
			
		}
		
		return reply;
		
	}


	public String getfullTypeName() {
		
		String reply = "";
		
		if (parent == null) {
			
			// This is the root node
			reply = name;
			
		} else {
			
			reply = parent.getfullTypeName() + "." + name;
			
		}
		
		return reply;
		
	}
	
}
